<?php

namespace app\common\library;

use think\Env;
use think\Config;
use think\View;

use app\admin\model\Admin;
use app\admin\model\Gateway;
use app\admin\model\Task;

use app\admin\model\ImsVlan;
use app\admin\model\ImsAccount;

if (!class_exists('ESL')) {
    require_once('esl/ESL.php');
}

class FSHelper
{
    protected $sock = NULL;

    protected $fsTplPath = NULL;
    protected $fsConfPath = NULL;

    protected $view = NULL;

    protected $queue_name_suffix = '@callback_queue';
    protected $agent_name_suffix = '@default';

    /**
     * 构造函数
     * NULL
     */
    public function __construct() {
        $this->fsTplPath = Env::get('fs.tpl.path', '');
        $this->fsConfPath = Env::get('fs.conf.path', '');
        $this->sock = new \ESLconnection('127.0.0.1', '8021', 'ClueCon');
    }
    
    public function __destruct() {
    }

    public function connected() {
        if ($this->sock == NULL) {
            return false;
        }
        return $this->sock->connected();
    }

    public function disconnect() {
        $this->sock->disconnect();
    }

    public function restart() {
        $cmd = '/usr/local/freeswitch/bin/fs_cli -x "fsctl shutdown restart" > /dev/null';
        system($cmd);
    }

    public function reloadXML() {
        $result = $this->sock->api('reloadxml');
        if (empty($result)) {
            return false;
        }
        $body = NULL;
        if (empty($body = $result->getBody())) {
            return false;
        }
        return ('+OK [Success]' == trim($body));
    }

    public function loadCallcenterQueue($queue_name) {
        $result = $this->sock->api('callcenter_config queue load ' . $queue_name);
        if (empty($result)) {
            return false;
        }
        $body = NULL;
        if (empty($body = $result->getBody())) {
            return false;
        }
        return ('+OK' == trim($body));
    }

    public function unloadCallcenterQueue($queue_name) {
        $result = $this->sock->api('callcenter_config queue unload ' . $queue_name);
        if (empty($result)) {
            return false;
        }
        $body = NULL;
        if (empty($body = $result->getBody())) {
            return false;
        }
        return ('+OK' == trim($body));
    }

    public function bgapi($cmd) {
        $result = $this->sock->bgapi($cmd);
        if (empty($result)) {
            return false;
        }
        $body = NULL;
        if (empty($body = $result->getBody())) {
            return false;
        }
        return (strpos($body, '+OK') === 0);
    }

    public function api($cmd) {
        $result = $this->sock->api($cmd);
        if (empty($result)) {
            return false;
        }
        $body = NULL;
        if (empty($body = $result->getBody())) {
            return false;
        }
        return (strpos($body, '+OK') === 0);
    }
    
    protected function safe_unlink($path) {
        if (!file_exists($path)) {
            return false;
        }
        return unlink($path);
    }

    public function addExtension($extension) {
        $result = false;
        if ($extension['status'] != 'normal') {
            return $result;
        }
        $this->view = new View();
        $this->view->assign('extension', $extension);
        $output = $this->view->fetch($this->fsTplPath . DS . 'extension.tpl');
        $out_file = $this->fsConfPath . DS . 'directory/default' . DS . $extension['extension'] . '.xml';
        $result = file_put_contents($out_file, $output);
        Db()->execute('insert into agents(name, system, type, contact, status, state) values(?, ?, ?, ?, ?, ?)', [$extension['extension'] . $this->agent_name_suffix, 'single_box', 'callback', 'user/' . $extension['extension'], 'Logged Out', 'Waiting']);
        //return $result;
    }
   
    public function delExtension($extension_number) {
        $del_file = $this->fsConfPath . DS . 'directory/default' . DS . $extension_number . '.xml';
        if (!file_exists($del_file)) {
            return false;
        }
        $result = $this->safe_unlink($del_file);
        Db()->execute('delete from agents where name = ?', [$extension_number . $this->agent_name_suffix]);
    }

    public function updateExtension($old_number, $extension) {
        if (($old_number != $extension['extension']) || ($extension['status'] != 'normal')) {
            $this->delExtension($old_number);
        }
        $this->addExtension($extension);
        $this->rebuildIncallDP('', $extension);
        $this->rebuildOutcallDP($old_number, $extension);
    }

    public function addGateway($gateway) {
        $result = false;
        if ($gateway['state'] != '1') {
            return $result;
        }
        $this->view = new View();
        $this->view->assign('gateway', $gateway);
        $output = '';
        $out_file = '';
        if ($gateway['register'] == 'receive') {
            $output = $this->view->fetch($this->fsTplPath . DS . 'gateway.tpl');
            $out_file = $this->fsConfPath . DS . 'directory/gateway' . DS . $gateway['username'] . '.xml';
        } else {
            $output = $this->view->fetch($this->fsTplPath . DS . 'gateway_send.tpl');
            $out_file = $this->fsConfPath . DS . 'sip_profiles/internal' . DS . $gateway['username'] . '.xml';
        }
        $result = file_put_contents($out_file, $output);
        $extension = Admin::get(['id' => $gateway['admin_id']]);
        $this->rebuildIncallDP('', $extension);
        $this->rebuildOutcallDP('', $extension);
    }
    
    public function delGateway($gateway_username) {
        $del_file = $this->fsConfPath . DS . 'directory/gateway' . DS . $gateway_username . '.xml';
        if (file_exists($del_file)) {
            $this->safe_unlink($del_file);
        } else {
            $del_file = $this->fsConfPath . DS . 'sip_profiles/internal' . DS . $gateway_username . '.xml';
            if (! file_exists($del_file)) {
                return false;
            }
            $this->safe_unlink($del_file);
        }
        $gateway = Gateway::get(['username' => $gateway_username]);
        $extension = Admin::get(['id' => $gateway['admin_id']]);
        $this->rebuildIncallDP($gateway_username, $extension);
        $this->rebuildOutcallDP('', $extension);
    }

    public function updateGateway($old_username, $gateway) {
	if (($old_username != $gateway['username']) || ($gateway['state'] != '1')) {
            $this->delGateway($old_username);
        }
        $this->addGateway($gateway);
        $extension = Admin::get(['id' => $gateway['admin_id']]);
        $this->rebuildIncallDP($old_username, $extension);
        $this->rebuildOutcallDP('', $extension);
    }

    public function rebuildIncallDP($old_username, $extension) {
        if (!empty($old_username)) {
            $del_file = $this->fsConfPath . DS . 'dialplan/incall' . DS . $old_username . '.xml';
            $this->safe_unlink($del_file);
        }
        if (is_string($extension)) {
            $extension = Admin::get(['extension' => $extension]);
        }
        if (empty($extension)) {
            return false;
        }
        //$extension = (array)$extension;
        $extension = json_decode(json_encode($extension), true);
        if ($extension['status'] != 'normal') {
            return false;
        }
        $gatewayList = Gateway::where('admin_id', '=', $extension['id'])->select();
        $this->view = new View();
        foreach ($gatewayList as $one) {
            if ($one['state'] != '1') {
                continue;
            }
            $this->view->assign('gateway', $one);
            $this->view->assign('extension', $extension);
            $output = $this->view->fetch($this->fsTplPath . DS . 'dialplan' . DS . 'incall.tpl');
        //print_r($output);
            $out_file = $this->fsConfPath . DS . 'dialplan/incall' . DS . $one['username'] . '.xml';
            file_put_contents($out_file, $output);
        }
    }
    
    public function rebuildOutcallDP($old_extension, $extension) {
        if (!empty($old_extension)) {
            $del_file = $this->fsConfPath . DS . 'dialplan/outcall' . DS . $old_extension . '.xml';
            $this->safe_unlink($del_file);
        }
        if (is_string($extension)) {
            $extension = Admin::get(['extension' => $extension]);
        }
        if (empty($extension)) {
            return false;
        }
        //$extension = (array)$extension;
        $extension = json_decode(json_encode($extension), true);
        if ($extension['status'] != 'normal') {
            return false;
        }
        $gatewayList = Gateway::where('admin_id', '=', $extension['id'])->where('state', '=', '1')->select();
        $gateways = collection($gatewayList);
        $this->view = new View();
        $this->view->assign('gateways', $gateways);
        $this->view->assign('extension', $extension);
        $output = $this->view->fetch($this->fsTplPath . DS . 'dialplan' . DS . 'outcall.tpl');
        $out_file = $this->fsConfPath . DS . 'dialplan/outcall' . DS . $extension['extension'] . '.xml';
        file_put_contents($out_file, $output);
    }

    public function prepareCallbackForTask($taskId) {
//        return true;
        //$task = Task::with('gateways,admins,sound')->where('task.id', '=', $taskId)->find();
        $task = Task::where('id', '=', $taskId)->find();
        if (empty($task)) {
            return;
        }
/*
        if (empty($task->admins)) {
            return;
        }

        $result = Db()->execute('delete from tiers where queue=?', [ 'TASK' . $task->id . $this->queue_name_suffix ]);

        foreach ($task->admins as $one) {

            $result = false;
            $result = Db()->query('select count(*) as count from agents where name = ?', [$one->extension . $this->agent_name_suffix]);
            //print_r($result);
            if ($result[0]['count'] <= 0) {
                $result = Db()->execute('insert into agents(name, system, type, contact, status, state) values(?, ?, ?, ?, ?, ?)', [$one->extension . $this->agent_name_suffix, 'single_box', 'callback', 'user/' . $one->extension, 'Available', 'Waiting']);
            } else {
                //$result = Db()->execute('update agents set status=?, state=? where name=?', ['Available', 'Waiting', $one->extension . $this->agent_name_suffix]);
            }
                
            $result = Db()->execute('insert into tiers(queue, agent, state) values(?, ?, ?)', [ 'TASK' . $task->id . $this->queue_name_suffix, $one->extension . $this->agent_name_suffix, 'Ready']);            

        }
*/
/*
        $this->view = new View();
        $this->view->assign('task', $task);
        $output = $this->view->fetch($this->fsTplPath . DS . 'callback/queue.tpl');
        $out_file = $this->fsConfPath . DS . 'callback/queue' . DS . $task->id . '.xml';
        $result = file_put_contents($out_file, $output);
*/

        $this->view = new View();
        $this->view->assign('task', $task);
        $speech_script_path = Env::get('speech.script.path', '/tmp');
        $speech_script_output_path = Env::get('speech.script.output.path', '/tmp');
        $this->view->assign('speech_script_path', $speech_script_path);
        $this->view->assign('speech_script_output_path', $speech_script_output_path);
//        $this->view->assign('sound', $task->sound);
        $output = $this->view->fetch($this->fsTplPath . DS . 'callback/dialplan.tpl');
        $out_file = $this->fsConfPath . DS . 'dialplan/callback' . DS . $task->id . '.xml';
        $result = file_put_contents($out_file, $output);

        $this->reloadXML();
        //$this->unloadCallcenterQueue('TASK' . $task->id . $this->queue_name_suffix);
//        $this->loadCallcenterQueue('TASK' . $task->id . $this->queue_name_suffix);

    }

    public function removeCallbackForTask($taskId) {
//        return true;
        $task = Task::where('id', '=', $taskId)->find();
        if (empty($task)) {
            return;
        }

//        $result = Db()->execute('delete from members where queue=?', ['TASK' . $task->id . $this->queue_name_suffix]);
//        $this->unloadCallcenterQueue('TASK' . $task->id . $this->queue_name_suffix);
//        $result = Db()->execute('delete from tiers where queue=?', ['TASK' . $task->id . $this->queue_name_suffix]);
//        $del_file = $this->fsConfPath . DS . 'callback/queue' . DS . $task->id . '.xml';
//        $this->safe_unlink($del_file);
//
        $del_file = $this->fsConfPath . DS . 'dialplan/callback' . DS . $task->id . '.xml';
        $this->safe_unlink($del_file);
        $this->reloadXML();
    }

    public function setAgentStatus($extension, $status) {
        $statusMap = array('busy' => 'Logged Out', 'idle' => 'Available');
        $result = Db()->execute('update agents set status = ? where name = ?', [$statusMap[$status], $extension . $this->agent_name_suffix]);
        return $result;
    }

    public function getAgentStatus($extension) {
        $statusMap = array('Logged Out' => 'busy', 'On Break' => 'busy', 'Available' => 'idle', 'Available (On Demand)' => 'idle');
        $result = Db()->query('select * from agents where name = ?', [$extension . $this->agent_name_suffix]);
        if (empty($result) || empty($result[0]) || empty($result[0]['status'])) {
            return false;
        }
        return $statusMap[$result[0]['status']];
    }

    public function getAvailableAgentCountByTask($taskId) {
        if (empty($taskId)) {
            return 0;
        }
        $queue_name = 'TASK' . $taskId . $this->queue_name_suffix;
        $result = $this->sock->api("callcenter_config queue count agents {$queue_name} Available");
        if (empty($result)) {
            return 0;
        }
        $body = NULL;
        if (empty($body = $result->getBody())) {
            return 0;
        }
        return intval($body);
    }

    public function addIMSExternalProfile($vlan) {
        $vlan = json_decode(json_encode($vlan), true);
        $result = false;
        $this->view = new View();
        $this->view->assign('vlan', $vlan);
        $output = $this->view->fetch($this->fsTplPath . DS . 'ims/external-profile.tpl');
        $out_file = $this->fsConfPath . DS . 'sip_profiles/ims' . DS . "ims-external-" . $vlan['vlanid'] . '.xml';
        $result = file_put_contents($out_file, $output);
        if (!is_dir($this->fsConfPath . DS . 'sip_profiles/ims/ims-external-' . $vlan['vlanid'])) {
            mkdir($this->fsConfPath . DS . 'sip_profiles/ims/ims-external-' . $vlan['vlanid']);
        }
    }
   
    public function delIMSExternalProfile($vlan) {
        $vlan = json_decode(json_encode($vlan), true);
        $del_file = $this->fsConfPath . DS . 'sip_profiles/ims' . DS . "ims-external-" . $vlan['vlanid'] . '.xml';
        if (!file_exists($del_file)) {
            return false;
        }
        $this->safe_unlink($del_file);
        if (is_dir($this->fsConfPath . DS . 'sip_profiles/ims/ims-external-' . $vlan['vlanid'])) {
            $cmd = 'rm -rf ' . $this->fsConfPath . DS . 'sip_profiles/ims/ims-external-' . $vlan['vlanid'];
            //die($cmd);
            system($cmd);
        }
    }

    public function rescanGateway($profile) {
        $result = $this->sock->api("sofia profile {$profile} rescan");
        if (empty($result)) {
            return false;
        }
        $body = NULL;
        if (empty($body = $result->getBody())) {
            return false;
        }
//var_dump($body);
//        return ('Reload XML [Success] +OK scan complete' == trim($body));
        return (strpos($body, '+OK scan complete') !== false);
    }

    public function killGateway($profile, $gateway) {
        $result = $this->sock->api("sofia profile {$profile} killgw {$gateway}");
        if (empty($result)) {
            return false;
        }
        $body = NULL;
        if (empty($body = $result->getBody())) {
            return false;
        }
//var_dump($body);
        //return ('+OK gateway marked for deletion.' == trim($body));
        return (strpos($body, '+OK gateway marked for deletion') !== false);
    }

    public function addIMSExternalGateway($account) {
        $account = json_decode(json_encode($account), true);
        $result = false;
        $vlan = ImsVlan::where('id', '=', $account['ims_vlan_id'])->find();
        if (empty($vlan)) {
            return $result;
        }
        $vlan = json_decode(json_encode($vlan), true);
        $this->view = new View();
        $this->view->assign('account', $account);
        $output = $this->view->fetch($this->fsTplPath . DS . 'ims/external-gateway.tpl');
        $out_file = $this->fsConfPath . DS . 'sip_profiles/ims' . DS . 'ims-external-' . $vlan['vlanid'] . DS . 'ims' . $account['ims_number'] . '.xml';
        $result = file_put_contents($out_file, $output);
        $this->reloadXML();
        $res = $this->rescanGateway('ims-external-' . $vlan['vlanid']);
        //var_dump($res);
    }

    public function delIMSExternalGateway($account) {
        $account = json_decode(json_encode($account), true);
        $result = false;
        $vlan = ImsVlan::where('id', '=', $account['ims_vlan_id'])->find();
        if (empty($vlan)) {
            return $result;
        }
        $del_file = $this->fsConfPath . DS . 'sip_profiles/ims' . DS . 'ims-external-' . $vlan['vlanid'] . DS . 'ims' . $account['ims_number'] . '.xml';
        if (!file_exists($del_file)) {
            return false;
        }
        $this->safe_unlink($del_file);
        $this->reloadXML();
        $res = $this->killGateway('ims-external-' . $vlan['vlanid'], 'ims' . $account['ims_number']);
        //var_dump($res);
    }

    public function addIMSInternalGateway($account) {
        $account = json_decode(json_encode($account), true);
        $result = false;
        $this->view = new View();
        $this->view->assign('account', $account);
        $output = $this->view->fetch($this->fsTplPath . DS . 'ims/internal-gateway.tpl');
        $out_file = $this->fsConfPath . DS . 'sip_profiles' . DS . 'ims-internal' . DS . $account['gateway_username'] . '.xml';
        $result = file_put_contents($out_file, $output);
        $this->reloadXML();
        $res = $this->rescanGateway('ims-internal');
        //var_dump($res);
    }

    public function delIMSInternalGateway($account) {
        $account = json_decode(json_encode($account), true);
        $result = false;
        $del_file = $this->fsConfPath . DS . 'sip_profiles' . DS . 'ims-internal' . DS . $account['gateway_username'] . '.xml';
        if (!file_exists($del_file)) {
            return false;
        }
        $this->safe_unlink($del_file);
        $this->reloadXML();
        $res = $this->killGateway('ims-internal', $account['gateway_username']);
        //var_dump($res);
    }

    public function addIMSDialPlan($account) {
        $account = json_decode(json_encode($account), true);
        $result = false;
        $this->view = new View();
        $this->view->assign('account', $account);
        $output = $this->view->fetch($this->fsTplPath . DS . 'ims/dialout-dialplan.tpl');
        $out_file = $this->fsConfPath . DS . 'dialplan' . DS . 'ims-default' . DS . 'ims' . $account['ims_number'] . '.xml';
        $result = file_put_contents($out_file, $output);
        $this->view = new View();
        $this->view->assign('account', $account);
        $output = $this->view->fetch($this->fsTplPath . DS . 'ims/dialin-dialplan.tpl');
        $out_file = $this->fsConfPath . DS . 'dialplan' . DS . 'ims-public' . DS . $account['gateway_username'] . '.xml';
        $result = file_put_contents($out_file, $output);
        $this->reloadXML();
        //var_dump($res);
    }

    public function delIMSDialPlan($account) {
        $account = json_decode(json_encode($account), true);
        $result = false;
        $del_file = $this->fsConfPath . DS . 'dialplan' . DS . 'ims-default' . DS . 'ims' . $account['ims_number'] . '.xml';
        if (!file_exists($del_file)) {
            return false;
        }
        $this->safe_unlink($del_file);
        $del_file = $this->fsConfPath . DS . 'dialplan' . DS . 'ims-public' . DS . $account['gateway_username'] . '.xml';
        if (!file_exists($del_file)) {
            return false;
        }
        $this->safe_unlink($del_file);
        $this->reloadXML();
        //var_dump($res);
    }

    public function addIMSAccount($account) {
        $this->addIMSExternalGateway($account);
        $this->addIMSInternalGateway($account);
        $this->addIMSDialPlan($account);
    }

    public function delIMSAccount($account) {
        $this->delIMSExternalGateway($account);
        $this->delIMSInternalGateway($account);
        $this->delIMSDialPlan($account);
    }

    public function getGatewayState($gateway) {
        $result = $this->sock->api("sofia status gateway {$gateway}");
        if (empty($result)) {
            return 'Err!';
        }
        $body = NULL;
        if (empty($body = $result->getBody())) {
            return 'Err!';
        }
        $pattern = '/\nState\s+([^\s]+)\n/i';
        $matches = null;
        $res = preg_match($pattern, $body, $matches);
        if ($res < 1) {
            return 'Err!';
        }
 
//var_dump($matches);
  
       return $matches[1];
//        return ('Reload XML [Success] +OK scan complete' == trim($body));
//        return (strpos($body, '+OK scan complete') !== false);
    }

    public function getRegState($reg) {
        $result = $this->sock->api("sofia status profile internal user {$reg}@");
        if (empty($result)) {
            return 'Err!';
        }
        $body = NULL;
        if (empty($body = $result->getBody())) {
            return 'Err!';
        }
//var_dump($body);
        $pattern = '/\nStatus:\s+(.+)\n/i';
        $matches = null;
        $res = preg_match($pattern, $body, $matches);
        if ($res < 1) {
            return 'Err!';
        }
               
//var_dump($matches);
        
       return $matches[1];
//        return ('Reload XML [Success] +OK scan complete' == trim($body));
//        return (strpos($body, '+OK scan complete') !== false);
    }

}
